package top.ffhome.game.ch05;

import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.util.Stack;

public class GameFrame extends JFrame implements MouseListener, KeyListener {
    private int grade = 0;
    private int row = 7, column = 7, leftX = 0, leftY = 0;
    private int mapRow = 0, mapColumn = 0;
    private boolean acceptKey = true;
    private Image pic[] = null;
    final byte WALL = 1, BOX = 2, BOXONEND = 3, END = 4, MANDOWN = 5,
            MANLEFT = 6, MANRIGHT = 7, MANUP = 8, GRASS = 9, MANDOWNONEND = 10,
            MANLEFTONEND = 11, MANRIGHTONEND = 12, MANUPONEND = 13;

    private byte[][] map = null;
    private Stack<Map> stack = new Stack<>();

    private Sound sound;

    private Image iBuffer;
    private Graphics gBuffer;

    public void displayToast(String str) {
        JOptionPane.showMessageDialog(null, str, "提示", JOptionPane.ERROR_MESSAGE);
    }

    public void undo() {
        if (acceptKey) {
            // 撤销
            if (!stack.empty()) {
                // 若要撤销 必须走过
                Map priorMap = stack.pop();
                map = priorMap.getMap();
                row = priorMap.getManX();
                column = priorMap.getManY();
                repaint();
            } else
                displayToast("不能再撤销！");
        } else {
            displayToast("此关已完成，不能撤销！");
        }
    }

    public void nextGrade() {
        if (grade >= MapFactory.getCount() - 1) {
            displayToast("恭喜你完成所有关卡！");
            acceptKey = false;
        } else {
            grade++;
            initMap();
            repaint();
            acceptKey = true;
        }
    }

    private void getManPosition() {
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[0].length; j++) {
                if (map[i][j] == MANDOWN || map[i][j] == MANDOWNONEND
                        || map[i][j] == MANUP || map[i][j] == MANUPONEND
                        || map[i][j] == MANLEFT || map[i][j] == MANLEFTONEND
                        || map[i][j] == MANRIGHT || map[i][j] == MANRIGHTONEND) {
                    row = i;
                    column = j;
                    break;
                }
            }
        }
    }

    public void initMap() {
        map = MapFactory.getMap(grade);
        stack.clear();
        getMapSizeAndPosition();
        getManPosition();
    }

    public GameFrame() {
        super("推箱子");
        setSize(600, 600);
        setVisible(true);
        setResizable(false);
        setLocation(300, 20);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Container cont = getContentPane();
        cont.setLayout(null);
        cont.setBackground(Color.black);
        getPic();
        this.setFocusable(true);
        initMap();
        this.addKeyListener(this);
        this.addMouseListener(this);
        sound = new Sound();
        sound.loadSound();
    }

    private void getMapSizeAndPosition() {
        mapRow = map.length;
        mapColumn = map[0].length;
        leftX = (this.getWidth() - mapColumn * 30) / 2;
        leftY = (this.getHeight() - mapRow * 30) / 2;
    }

    public void getPic() {
        String path = this.getClass().getClassLoader().getResource("").getPath();
        pic = new Image[14];
        for (int i = 0; i <= 13; i++) {
            pic[i] = Toolkit.getDefaultToolkit().getImage(path + "ch05/images/pic" + i + ".jpg");
        }
    }

    public byte grassOrEnd(byte man) {
        if (man == MANDOWNONEND || man == MANLEFTONEND || man == MANRIGHTONEND || man == MANUPONEND) {
            return END;
        }
        return GRASS;
    }

    private void moveUp() {
        // 上一位p1为WALL
        if (map[row - 1][column] == WALL)
            return;
        // 上一位p1为 BOX,BOXONEND,须考虑P2
        if (map[row - 1][column] == BOX || map[row - 1][column] == BOXONEND) {
            // 上上一位p2为 END,GRASS则向上一步,其他不用处理
            if (map[row - 2][column] == END || map[row - 2][column] == GRASS) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte boxTemp = map[row - 2][column] == END ? BOXONEND : BOX;
                byte manTemp = map[row - 1][column] == BOX ? MANUP : MANUPONEND;
                // 箱子变成temp,箱子往前一步
                map[row - 2][column] = boxTemp;
                // 人变成MANUP,往上走一步
                map[row - 1][column] = manTemp;
                // 人刚才站的地方变成GRASS或者END
                map[row][column] = grassOrEnd(map[row][column]);
                // 人离开后修改人的坐标
                row--;
            }
        } else {
            // 上一位为 GRASS,END,不须考虑P2。其他情况不用处理
            if (map[row - 1][column] == GRASS || map[row - 1][column] == END) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte temp = map[row - 1][column] == END ? MANUPONEND : MANUP;
                // 人变成temp,人往上走一步
                map[row - 1][column] = temp;
                // 人刚才站的地方变成GRASS或者END
                map[row][column] = grassOrEnd(map[row][column]);
                // 人离开后修改人的坐标
                row--;
            }
        }
    }

    private void moveDown() {
        // 下一位p1为WALL
        if (map[row + 1][column] == WALL)
            return;
        // 下一位p1为 BOX,BOXONEND,须考虑P2
        if (map[row + 1][column] == BOX || map[row + 1][column] == BOXONEND) {
            // 下下一位p2为 END,GRASS则向下一步,其他不用处理
            if (map[row + 2][column] == END || map[row + 2][column] == GRASS) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte boxTemp = map[row + 2][column] == END ? BOXONEND : BOX;
                byte manTemp = map[row + 1][column] == BOX ? MANDOWN : MANDOWNONEND;
                // 箱子变成boxTemp,箱子往下一步
                map[row + 2][column] = boxTemp;
                // 人变成manTemp,往下走一步
                map[row + 1][column] = manTemp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                row++;
            }

        } else {
            // 下一位为 GRASS,END,不须考虑P2,其他情况不用处理
            if (map[row + 1][column] == GRASS || map[row + 1][column] == END) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte temp = map[row + 1][column] == END ? MANDOWNONEND : MANDOWN;
                // 人变成temp,人往下走一步
                map[row + 1][column] = temp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                row++;

            }
        }
    }

    private void moveLeft() {
        // 左一位p1为WALL
        if (map[row][column - 1] == WALL)
            return;
        // 左一位p1为 BOX,BOXONEND
        if (map[row][column - 1] == BOX || map[row][column - 1] == BOXONEND) {
            // 左左一位p2为 END,GRASS则向左一步,其他不用处理
            if (map[row][column - 2] == END || map[row][column - 2] == GRASS) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte boxTemp = map[row][column - 2] == END ? BOXONEND : BOX;
                byte manTemp = map[row][column - 1] == BOX ? MANLEFT : MANLEFTONEND;
                // 箱子变成boxTemp,箱子往左一步
                map[row][column - 2] = boxTemp;
                // 人变成manTemp,往左走一步
                map[row][column - 1] = manTemp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                column--;
            }
        } else {
            // 左一位为 GRASS,END,其他情况不用处理
            if (map[row][column - 1] == GRASS || map[row][column - 1] == END) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte temp = map[row][column - 1] == END ? MANLEFTONEND : MANLEFT;
                // 人变成temp,人往左走一步
                map[row][column - 1] = temp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                column--;

            }
        }
    }

    private void moveRight() {
        // 右一位p1为WALL
        if (map[row][column + 1] == WALL)
            return;
        // 右一位为 BOX,BOXONEND
        if (map[row][column + 1] == BOX || map[row][column + 1] == BOXONEND) {
            // 右右一位p2为 END,GRASS则向右一步,其他不用处理
            if (map[row][column + 2] == END || map[row][column + 2] == GRASS) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte boxTemp = map[row][column + 2] == END ? BOXONEND : BOX;
                byte manTemp = map[row][column + 1] == BOX ? MANRIGHT : MANRIGHTONEND;
                // 箱子变成boxTemp,箱子往右一步
                map[row][column + 2] = boxTemp;
                // 人变成manTemp,往右走一步
                map[row][column + 1] = manTemp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                column++;
            }
        } else {
            // 右一位为 GRASS,END,其他情况不用处理
            if (map[row][column + 1] == GRASS || map[row][column + 1] == END) {
                Map currMap = new Map(row, column, map);
                stack.push(currMap);
                byte temp = map[row][column + 1] == END ? MANRIGHTONEND : MANRIGHT;
                // 人变成temp,人往右走一步
                map[row][column + 1] = temp;
                // 人刚才站的地方变成 grassOrEnd(map[row][column])
                map[row][column] = grassOrEnd(map[row][column]);
                column++;

            }
        }
    }

    public boolean isFinished() {
        for (int i = 0; i < mapRow; i++)
            for (int j = 0; j < mapColumn; j++)
                if (map[i][j] == END || map[i][j] == MANDOWNONEND
                        || map[i][j] == MANUPONEND || map[i][j] == MANLEFTONEND
                        || map[i][j] == MANRIGHTONEND)
                    return false;
        return true;
    }

    @Override
    public void paint(Graphics g) {
        if (iBuffer == null) {
            iBuffer = createImage(this.getSize().width, this.getSize().height);
            gBuffer = iBuffer.getGraphics();
        }

        gBuffer.setColor(getBackground());
        gBuffer.fillRect(0, 0, this.getSize().width, this.getSize().height);
        for (int i = 0; i < mapRow; i++) {
            for (int j = 0; j < mapColumn; j++) {
                if (map[i][j] != 0) {
                    gBuffer.drawImage(pic[map[i][j]], leftX + j * 30, leftY + i * 30, this);
                }
            }
        }
        gBuffer.setColor(Color.RED);
        gBuffer.setFont(new Font("楷体_2312", Font.BOLD, 30));
        gBuffer.drawString("现在是第", 150, 140);
        gBuffer.drawString(String.valueOf(grade + 1), 310, 140);
        gBuffer.drawString("关", 360, 140);

        g.drawImage(iBuffer, 0, 0, this);
    }

    public static void main(String[] args) {
        new GameFrame();
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_UP) {
            moveUp();
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            moveDown();
        } else if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            moveLeft();
        } else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            moveRight();
        }
        repaint();
        if (isFinished()) {
            acceptKey = false;
            if (grade == 10) {
                JOptionPane.showMessageDialog(this, "恭喜您通过最后一关！！！");
            } else {
                // 提示进入下一关
                String msg = "恭喜您通过第" + grade + "关!!!\n是否要进入下一关？";
                int type = JOptionPane.YES_NO_OPTION;
                String title = "过关";
                int choice = 0;
                choice = JOptionPane.showConfirmDialog(null, msg, title, type);
                if (choice == 1)
                    System.exit(0);
                else if (choice == 0) {
                    // 进入下一关
                    acceptKey = true;
                    nextGrade();
                }
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON3) {// 右键撤销移动
            undo();// 撤销移动
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
    }

    @Override
    public void mouseReleased(MouseEvent e) {
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
}

class Sound// 播放背景音乐
{
    String path = this.getClass().getClassLoader().getResource("").getPath();
    String file = new String("ch05/musics/nor.mid");
    Sequence seq;
    Sequencer midi;
    boolean sign;

    void loadSound() {
        try {
            seq = MidiSystem.getSequence(new File(path + file));
            midi = MidiSystem.getSequencer();
            midi.open();
            midi.setSequence(seq);
            midi.start();
            midi.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        sign = true;
    }

    void myStop() {
        midi.stop();
        midi.close();
        sign = false;
    }

    boolean isPlay() {
        return sign;
    }

    void setMusic(String e) {
        file = e;
    }
}